using System.IO;
using System.Xml;

namespace MockingBird.Studio.UI
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.ServiceModel.Description;
    using System.Windows.Forms;
    using MockingBird.Simulator.Framework.Configuration;
    using MockingBird.Simulator.Management;
    using MockingBird.Studio.UI.Helpers;
    using MockingBird.Studio.UI.UserControls;
    using MockingBird.Studio.UI.UserControls.Model;
    using Thinktecture.Tools.Wscf.Services.ServiceDescription.Contract;


    public partial class Configurator : ComponentFactory.Krypton.Toolkit.KryptonForm
    {
        private ServiceDefinition serviceDefinition;
        private const string GENERATE_DEFAULT = "GenerateDefaultConfig";
        private string simulatorRootFolder;
        private string endpointName;
        private string configFileName;
        private string handlerMapFileName;
        private OperationInferenceStrategy inferenceStrategy;
        private List<OperationMapEntryType> requestRootMap;
        private List<OperationType> operationMap;

        private bool hasConfigBeenGenerated = false;
        private List<string> requestMsgNames;
        private List<string> responseMsgNames;
        private List<XmlDocument> requestMessages;
        private List<XmlDocument> responseMessages;

        #region "component dependencies"
        private IServiceBuilder serviceBuilder;
        private ApplicationConfiguration simulatorAppConfig;
        private IHandlerConfigurationManager handlerConfigManager;
        private IFolderManager folderManager;
        #endregion

        #region "constructor(s)"
        public Configurator()
        {
            InitializeComponent();
            this.tvwWsdlTree.WsdlMenuClicked += new WsdlTreeEventHandler(WsdlMenuItemClicked);
            this.InitializeDependencies();
        }
        #endregion

        #region "event handlers"

        private void HandleFormLoad()
        {
            txtWsdlUrl.Text = ConfigurationManager.AppSettings["default_config_wsdl_location"];
            InitializeDialogProperties();
        }

        private void WsdlMenuItemClicked(object sender, WsdlTreeEventArgs e)
        {
            this.ProcessCommandActions(e.Command, e.ItemName);
        }


        #endregion

        #region "helper methods"

        private void InitializeDependencies()
        {
            this.simulatorAppConfig = DependencyHelper.GetAppConfig();
            this.handlerConfigManager = DependencyHelper.GetHandlerConfigManager(this.simulatorAppConfig);
            this.folderManager = DependencyHelper.GetFolderManager();

        }

        private void InitializeDialogProperties()
        {
            // WSDL Browse Dialog
            dlgWsdlBrowse.DefaultExt = "*.wsdl";
            dlgWsdlBrowse.Filter = "WSDL files (*.wsdl)|*.wsdl";
            dlgWsdlBrowse.FileName = "";
            dlgWsdlBrowse.InitialDirectory = Path.GetDirectoryName(txtWsdlUrl.Text);

            // Handler Map File Browse dialog
            dlgHandlerMapFileBrowse.DefaultExt = "*.config";
            dlgHandlerMapFileBrowse.Filter = "CONFIG files (*.config)|*.config";
            dlgHandlerMapFileBrowse.FileName = "";


            // default root location
            if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["default_mb_root"]))
            {
                txtSimulatorRoot.Text = ConfigurationManager.AppSettings["default_mb_root"];
            }

            // set the base folder to the same as the simulator root.
            dlgSimulatorRootFolderBrowse.SelectedPath = txtSimulatorRoot.Text;

        }

        private void LoadWsdl()
        {
            try
            {
                string wsdlUrl = txtWsdlUrl.Text;
                this.serviceDefinition = MetadataHelper.ImportWsdl(wsdlUrl);
                WsdlDocumentCommands commands = this.GetWsdlCommands();
                this.tvwWsdlTree.LoadTree(this.serviceDefinition, commands);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.ToString());
                txtWsdlUrl.Focus();
                return;
            }
        }

        private WsdlDocumentCommands GetWsdlCommands()
        {
            WsdlDocumentCommands commands = new WsdlDocumentCommands();
            commands.ServiceCommands = new List<CommandAction>();

            // Should load from configuration. for now we set the standard data

            CommandAction generateSample = new CommandAction();
            generateSample.CommandName = GENERATE_DEFAULT;
            generateSample.DisplayName = GENERATE_DEFAULT;
            generateSample.Action = GENERATE_DEFAULT;
            commands.ServiceCommands.Add(generateSample);

            return commands;
        }

        private void ProcessCommandActions(CommandAction action, string itemName)
        {
            switch (action.CommandName)
            {
                case GENERATE_DEFAULT:
                    this.GenerateDefaultConfiguration();
                    break;

                default:
                    break;
            }
        }

        #endregion

        #region "Main Action methods"
        private void GenerateDefaultConfiguration()
        {
            //this.InferEndpointName();
            //this.InferConfigFilePath();
            this.SetOperationInference();
            this.SetRootElementMap();
            this.SetOperationStrategies();
            this.hasConfigBeenGenerated = true;
        }

        /// <summary>
        /// Attempts to infer the name of the endpoint.
        /// </summary>
        private void InferEndpointName()
        {
            string wsdlPath = txtWsdlUrl.Text;
            Uri wsdlUri = new Uri(wsdlPath);

            /* if this is a local file, then chances are that the file name is something.wsdl
             * in which case the service name is "something"
             * if it is from a mex endpoint or http endpoint then it is more involved
             * eg) http://localhost/customerservice/customer.asmx?wsdl
             * eg) http://localhost/managementservice/management.svc?wsdl
             * in all situations we will just swallow any exceptions and allow the user to 
             * enter the name if we cant infer it
             * */

            try
            {
                if (wsdlUri.IsFile)
                {
                    this.endpointName = System.IO.Path.GetFileNameWithoutExtension(wsdlUri.LocalPath);
                }
                else if (wsdlPath.Contains(".asmx?wsdl") || wsdlPath.Contains(".svc?wsdl"))
                {
                    this.endpointName = System.IO.Path.GetFileNameWithoutExtension(wsdlUri.LocalPath);
                }
                else if (wsdlPath.Contains("/MEX"))
                {
                    this.endpointName = wsdlUri.Segments[wsdlUri.Segments.Length - 2].TrimEnd("/".ToCharArray());
                }
                else
                {
                    this.endpointName = string.Empty;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }

            txtEndpointName.Text = this.endpointName;

        }

        private void InferConfigFilePath()
        {
            if (!String.IsNullOrEmpty(this.endpointName))
            {
                this.SetConfigFilePath();
            }
        }


        private void SetConfigFilePath()
        {
            // we shouldnt really need to put a full path here as there can be any number of 
            // custom settings applied later. (even currently, we need to use \\datafiles\\endpoints\\
            // and so on. That is too much knowledge to store here
            this.simulatorRootFolder = txtSimulatorRoot.Text;
            this.endpointName = txtEndpointName.Text;
            this.configFileName = this.endpointName + ".config";
            txtConfigFileName.Text = this.configFileName;

        }

        private void SetHandlerMapFilePath()
        {
            string configRoot = ConfigurationManager.AppSettings["mockingbird_configuration_folder"];
            string handlerMapFile = Path.Combine(configRoot, "handlerMap.config");
            txtHandlerMapFile.Text = handlerMapFile;

        }

        private void SetOperationInference()
        {
            string[] opItems = new string[] { "SoapAction", "QueryString", "RequestRootElementMap", "SoapActionOperationMap" };
            cboOperationInference.Items.Clear();
            cboOperationInference.Items.AddRange(opItems);
            //cboOperationInference.Items.Add("QueryString");
            //cboOperationInference.Items.Add("RequestRootElementMap");
            //cboOperationInference.Items.Add("SoapActionOperationMap");

            cboOperationInference.SelectedIndex = 2;
            this.inferenceStrategy = OperationInferenceStrategy.RequestRootElementOperationMap;


        }

        private void SetRootElementMap()
        {
            // Build the map from the service definition
            this.requestRootMap = this.BuildRootElementMapFromMetadata();

            gridMap.Columns.Clear();
            gridMap.DataSource = requestRootMap;
        }

        private void SetOperationStrategies()
        {
            List<BindableOperationEntry> opMap = this.BuildOperationsListFromMetadata();
            gridOperationSpec.Columns.Clear();
            gridOperationSpec.DataSource = opMap;
            gridOperationSpec.Columns[1].ReadOnly = true;

            // assign the internal operation map
            this.ConvertBindableOperations(opMap);

        }


        private List<OperationMapEntryType> BuildRootElementMapFromMetadata()
        {
            List<OperationMapEntryType> requestRootMap = new List<OperationMapEntryType>();
            OperationMapEntryType entry;
            foreach (OperationDescription op in this.serviceDefinition.Operations)
            {
                entry = new OperationMapEntryType();
                entry.OperationName = op.Name;
                MessageDescription md = ServiceDefinition.FindOperationMessage(op, MessageDirection.Input);
                entry.Key = ServiceDefinition.GetMessageName(md);
                requestRootMap.Add(entry);
            }

            return requestRootMap;
        }


        private List<BindableOperationEntry> BuildOperationsListFromMetadata()
        {
            List<BindableOperationEntry> opMap = new List<BindableOperationEntry>();
            BindableOperationEntry entry;
            foreach (OperationDescription op in this.serviceDefinition.Operations)
            {
                entry = new BindableOperationEntry();
                entry.OperationName = op.Name;
                if (op.IsOneWay) entry.IsOneWay = true;
                entry.ResponseBehavior = ResponseBehaviourType.Standard;
                entry.ErrorType = MockingBird.Simulator.Framework.Configuration.ErrorType.None;
                if (op.IsOneWay)
                {
                    entry.FileName = "--NA--";
                }
                else
                {
                    entry.FileName = op.Name + "Response.xml";
                }

                opMap.Add(entry);
            }

            return opMap;
        }

        private void ConvertBindableOperations(List<BindableOperationEntry> sourceMap)
        {
            this.operationMap = new List<OperationType>();
            OperationType opType;
            // we do this because the 'real' model is different from the view model
            foreach (BindableOperationEntry entry in sourceMap)
            {
                opType = new OperationType();
                opType.Name = entry.OperationName;
                if (entry.IsOneWay)
                {
                    opType.ResponseStrategy = ResponseStrategyType.None;
                }
                else
                {
                    opType.ResponseStrategy = ResponseStrategyType.Default;
                    ResponseType defResponse = new ResponseType();
                    defResponse.ResponseBehaviour = ResponseBehaviourType.Standard;
                    defResponse.FileName = entry.FileName;
                    defResponse.ErrorType = MockingBird.Simulator.Framework.Configuration.ErrorType.None;

                    opType.Item = defResponse;
                    opType.ItemElementName = ItemChoiceType.DefaultResponse;
                }


                this.operationMap.Add(opType);
            }
        }

        #endregion

        private void txtEndpointName_Leave(object sender, EventArgs e)
        {
            this.SetConfigFilePath();
        }

        private void btnCreateConfiguration_Click(object sender, EventArgs e)
        {
            this.HandleServiceAndConfigCreationEvent();


        }

        private void HandleServiceAndConfigCreationEvent()
        {


            try
            {
                this.ValidateConfigCreation();
                ServiceConfigurationSettings configSettings = this.GetConfigSettings();
                HandlerConfiguration configData = this.GetConfigData();

                this.BuildService(configSettings, configData);
                string successMessage = string.Format("Mock Endpoint {0} successfully created", configSettings.EndpointName);
                MessageBox.Show(successMessage);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

        }


        private void ValidateConfigCreation()
        {
            if (!this.hasConfigBeenGenerated)
            {
                throw new InvalidOperationException("Config has not yet been created for this service endpoint. Please ensure that you right-click on the endpoint in the tree and select 'GenerateDefaultConfig' ");
            }
        }

        private ServiceConfigurationSettings GetConfigSettings()
        {
            ServiceConfigurationSettings configSettings = new ServiceConfigurationSettings();
            configSettings.SimulatorRoot = this.simulatorRootFolder;
            configSettings.EndpointName = this.endpointName;
            configSettings.ConfigFileName = this.configFileName;
            configSettings.HandlerMapFileName = this.handlerMapFileName;
            configSettings.CreateServiceFolders = true;
            return configSettings;
        }

        private HandlerConfiguration GetConfigData()
        {
            HandlerConfiguration configData = new HandlerConfiguration();
            configData.ControlSettings = new HandlerConfigurationControlSettings();
            configData.ControlSettings.OperationInferenceStrategy = this.inferenceStrategy;
            configData.RequestRootElementOperationMap = new List<OperationMapEntryType>();
            configData.RequestRootElementOperationMap = this.requestRootMap;
            configData.Operations = new List<OperationType>();
            configData.Operations = this.operationMap;
            return configData;
        }

        private void BuildService(ServiceConfigurationSettings configSettings, HandlerConfiguration configData)
        {
            //this.serviceBuilder = new ServiceBuilder(configSettings);
            //this.serviceBuilder = DependencyHelper.GetServiceBuilder(configSettings);
            this.serviceBuilder = DependencyHelper.GetServiceBuilder(this.folderManager, this.handlerConfigManager, this.simulatorAppConfig, configSettings);
            this.serviceBuilder.UpdateConfigSettings(configSettings);
            this.serviceBuilder.CreateServiceArtifacts();
            this.serviceBuilder.CreateEndpointConfiguration(configData);
            this.serviceBuilder.CreateHandlerMapping();
            this.GenerateRequestMessages();
            this.GenerateResponseMessages();
            this.serviceBuilder.SaveRequestMessages(this.requestMessages);
            this.serviceBuilder.SaveResponseMessages(this.responseMessages);
        }

        private void GenerateRequestMessages()
        {
            this.requestMsgNames = this.tvwWsdlTree.GetRequestMessageNames();
            List<MessageDescription> collection = new List<MessageDescription>();
            foreach (string msg in this.requestMsgNames)
            {
                MessageDescription md = this.serviceDefinition.FindMessage(msg);
                collection.Add(md);
            }
            this.requestMessages = MessageHelper.GenerateInstances(collection, this.serviceDefinition.SchemaSet);
        }

        private void GenerateResponseMessages()
        {
            this.responseMsgNames = this.tvwWsdlTree.GetResponseMessageNames();
            List<MessageDescription> collection = new List<MessageDescription>();
            foreach (string msg in this.responseMsgNames)
            {
                MessageDescription md = this.serviceDefinition.FindMessage(msg);
                collection.Add(md);
            }
            this.responseMessages = MessageHelper.GenerateInstances(collection, this.serviceDefinition.SchemaSet);
        }



    }
}